/* fuc microcode util functions for nvc0 PGRAPH
 *
 * Copyright 2011 Red Hat Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Authors: Ben Skeggs
 */

define(`mmctx_data', `.b32 eval((($2 - 1) << 26) | $1)')
define(`queue_init', `.skip eval((2 * 4) + ((8 * 4) * 2))')

ifdef(`include_code', `
// Error codes
define(`E_BAD_COMMAND', 0x01)
define(`E_CMD_OVERFLOW', 0x02)

// Util macros to help with debugging ucode hangs etc
define(`T_WAIT', 0)
define(`T_MMCTX', 1)
define(`T_STRWAIT', 2)
define(`T_STRINIT', 3)
define(`T_AUTO', 4)
define(`T_CHAN', 5)
define(`T_LOAD', 6)
define(`T_SAVE', 7)
define(`T_LCHAN', 8)
define(`T_LCTXH', 9)

define(`trace_set', `
	mov $r8 0x83c
	shl b32 $r8 6
	clear b32 $r9
	bset $r9 $1
	iowr I[$r8 + 0x000] $r9		// CC_SCRATCH[7]
')

define(`trace_clr', `
	mov $r8 0x85c
	shl b32 $r8 6
	clear b32 $r9
	bset $r9 $1
	iowr I[$r8 + 0x000] $r9		// CC_SCRATCH[7]
')

// queue_put - add request to queue
//
// In : $r13 queue pointer
//	$r14 command
//	$r15 data
//
queue_put:
	// make sure we have space..
	ld b32 $r8 D[$r13 + 0x0]	// GET
	ld b32 $r9 D[$r13 + 0x4]	// PUT
	xor $r8 8
	cmpu b32 $r8 $r9
	bra ne #queue_put_next
		mov $r15 E_CMD_OVERFLOW
		call #error
		ret

	// store cmd/data on queue
	queue_put_next:
	and $r8 $r9 7
	shl b32 $r8 3
	add b32 $r8 $r13
	add b32 $r8 8
	st b32 D[$r8 + 0x0] $r14
	st b32 D[$r8 + 0x4] $r15

	// update PUT
	add b32 $r9 1
	and $r9 0xf
	st b32 D[$r13 + 0x4] $r9
	ret

// queue_get - fetch request from queue
//
// In : $r13 queue pointer
//
// Out:	$p1  clear on success (data available)
//	$r14 command
// 	$r15 data
//
queue_get:
	bset $flags $p1
	ld b32 $r8 D[$r13 + 0x0]	// GET
	ld b32 $r9 D[$r13 + 0x4]	// PUT
	cmpu b32 $r8 $r9
	bra e #queue_get_done
		// fetch first cmd/data pair
		and $r9 $r8 7
		shl b32 $r9 3
		add b32 $r9 $r13
		add b32 $r9 8
		ld b32 $r14 D[$r9 + 0x0]
		ld b32 $r15 D[$r9 + 0x4]

		// update GET
		add b32 $r8 1
		and $r8 0xf
		st b32 D[$r13 + 0x0] $r8
		bclr $flags $p1
queue_get_done:
	ret

// nv_rd32 - read 32-bit value from nv register
//
// In : $r14 register
// Out: $r15 value
//
nv_rd32:
	mov $r11 0x728
	shl b32 $r11 6
	mov b32 $r12 $r14
	bset $r12 31			// MMIO_CTRL_PENDING
	iowr I[$r11 + 0x000] $r12	// MMIO_CTRL
	nv_rd32_wait:
		iord $r12 I[$r11 + 0x000]
		xbit $r12 $r12 31
		bra ne #nv_rd32_wait
	mov $r10 6			// DONE_MMIO_RD
	call #wait_doneo
	iord $r15 I[$r11 + 0x100]	// MMIO_RDVAL
	ret

// nv_wr32 - write 32-bit value to nv register
//
// In : $r14 register
//      $r15 value
//
nv_wr32:
	mov $r11 0x728
	shl b32 $r11 6
	iowr I[$r11 + 0x200] $r15	// MMIO_WRVAL
	mov b32 $r12 $r14
	bset $r12 31			// MMIO_CTRL_PENDING
	bset $r12 30			// MMIO_CTRL_WRITE
	iowr I[$r11 + 0x000] $r12	// MMIO_CTRL
	nv_wr32_wait:
		iord $r12 I[$r11 + 0x000]
		xbit $r12 $r12 31
		bra ne #nv_wr32_wait
	ret

// (re)set watchdog timer
//
// In : $r15 timeout
//
watchdog_reset:
	mov $r8 0x430
	shl b32 $r8 6
	bset $r15 31
	iowr I[$r8 + 0x000] $r15
	ret

// clear watchdog timer
watchdog_clear:
	mov $r8 0x430
	shl b32 $r8 6
	iowr I[$r8 + 0x000] $r0
	ret

// wait_done{z,o} - wait on FUC_DONE bit to become clear/set
//
// In : $r10 bit to wait on
//
define(`wait_done', `
$1:
	trace_set(T_WAIT);
	mov $r8 0x818
	shl b32 $r8 6
	iowr I[$r8 + 0x000] $r10	// CC_SCRATCH[6] = wait bit
	wait_done_$1:
		mov $r8 0x400
		shl b32 $r8 6
		iord $r8 I[$r8 + 0x000]	// DONE
		xbit $r8 $r8 $r10
		bra $2 #wait_done_$1
	trace_clr(T_WAIT)
	ret
')
wait_done(wait_donez, ne)
wait_done(wait_doneo, e)

// mmctx_size - determine size of a mmio list transfer
//
// In : $r14 mmio list head
//      $r15 mmio list tail
// Out: $r15 transfer size (in bytes)
//
mmctx_size:
	clear b32 $r9
	nv_mmctx_size_loop:
		ld b32 $r8 D[$r14]
		shr b32 $r8 26
		add b32 $r8 1
		shl b32 $r8 2
		add b32 $r9 $r8
		add b32 $r14 4
		cmpu b32 $r14 $r15
		bra ne #nv_mmctx_size_loop
	mov b32 $r15 $r9
	ret

// mmctx_xfer - execute a list of mmio transfers
//
// In : $r10 flags
//		bit 0: direction (0 = save, 1 = load)
//		bit 1: set if first transfer
//		bit 2: set if last transfer
//	$r11 base
//	$r12 mmio list head
//	$r13 mmio list tail
//	$r14 multi_stride
//	$r15 multi_mask
//
mmctx_xfer:
	trace_set(T_MMCTX)
	mov $r8 0x710
	shl b32 $r8 6
	clear b32 $r9
	or $r11 $r11
	bra e #mmctx_base_disabled
		iowr I[$r8 + 0x000] $r11	// MMCTX_BASE
		bset $r9 0			// BASE_EN
	mmctx_base_disabled:
	or $r14 $r14
	bra e #mmctx_multi_disabled
		iowr I[$r8 + 0x200] $r14 	// MMCTX_MULTI_STRIDE
		iowr I[$r8 + 0x300] $r15 	// MMCTX_MULTI_MASK
		bset $r9 1			// MULTI_EN
	mmctx_multi_disabled:
	add b32 $r8 0x100

	xbit $r11 $r10 0
	shl b32 $r11 16			// DIR
	bset $r11 12			// QLIMIT = 0x10
	xbit $r14 $r10 1
	shl b32 $r14 17
	or $r11 $r14			// START_TRIGGER
	iowr I[$r8 + 0x000] $r11	// MMCTX_CTRL

	// loop over the mmio list, and send requests to the hw
	mmctx_exec_loop:
		// wait for space in mmctx queue
		mmctx_wait_free:
			iord $r14 I[$r8 + 0x000] // MMCTX_CTRL
			and $r14 0x1f
			bra e #mmctx_wait_free

		// queue up an entry
		ld b32 $r14 D[$r12]
		or $r14 $r9
		iowr I[$r8 + 0x300] $r14
		add b32 $r12 4
		cmpu b32 $r12 $r13
		bra ne #mmctx_exec_loop

	xbit $r11 $r10 2
	bra ne #mmctx_stop
		// wait for queue to empty
		mmctx_fini_wait:
			iord $r11 I[$r8 + 0x000]	// MMCTX_CTRL
			and $r11 0x1f
			cmpu b32 $r11 0x10
			bra ne #mmctx_fini_wait
		mov $r10 2				// DONE_MMCTX
		call #wait_donez
		bra #mmctx_done
	mmctx_stop:
		xbit $r11 $r10 0
		shl b32 $r11 16			// DIR
		bset $r11 12			// QLIMIT = 0x10
		bset $r11 18			// STOP_TRIGGER
		iowr I[$r8 + 0x000] $r11	// MMCTX_CTRL
		mmctx_stop_wait:
			// wait for STOP_TRIGGER to clear
			iord $r11 I[$r8 + 0x000] // MMCTX_CTRL
			xbit $r11 $r11 18
			bra ne #mmctx_stop_wait
	mmctx_done:
	trace_clr(T_MMCTX)
	ret

// Wait for DONE_STRAND
//
strand_wait:
	push $r10
	mov $r10 2
	call #wait_donez
	pop $r10
	ret

// unknown - call before issuing strand commands
//
strand_pre:
	mov $r8 0x4afc
	sethi $r8 0x20000
	mov $r9 0xc
	iowr I[$r8] $r9
	call #strand_wait
	ret

// unknown - call after issuing strand commands
//
strand_post:
	mov $r8 0x4afc
	sethi $r8 0x20000
	mov $r9 0xd
	iowr I[$r8] $r9
	call #strand_wait
	ret

// Selects strand set?!
//
// In: $r14 id
//
strand_set:
	mov $r10 0x4ffc
	sethi $r10 0x20000
	sub b32 $r11 $r10 0x500
	mov $r12 0xf
	iowr I[$r10 + 0x000] $r12		// 0x93c = 0xf
	mov $r12 0xb
	iowr I[$r11 + 0x000] $r12		// 0x928 = 0xb
	call #strand_wait
	iowr I[$r10 + 0x000] $r14		// 0x93c = <id>
	mov $r12 0xa
	iowr I[$r11 + 0x000] $r12		// 0x928 = 0xa
	call #strand_wait
	ret

// Initialise strand context data
//
// In : $r15 context base
// Out: $r15 context size (in bytes)
//
// Strandset(?) 3 hardcoded currently
//
strand_ctx_init:
	trace_set(T_STRINIT)
	call #strand_pre
	mov $r14 3
	call #strand_set
	mov $r10 0x46fc
	sethi $r10 0x20000
	add b32 $r11 $r10 0x400
	iowr I[$r10 + 0x100] $r0	// STRAND_FIRST_GENE = 0
	mov $r12 1
	iowr I[$r11 + 0x000] $r12	// STRAND_CMD = LATCH_FIRST_GENE
	call #strand_wait
	sub b32 $r12 $r0 1
	iowr I[$r10 + 0x000] $r12	// STRAND_GENE_CNT = 0xffffffff
	mov $r12 2
	iowr I[$r11 + 0x000] $r12	// STRAND_CMD = LATCH_GENE_CNT
	call #strand_wait
	call #strand_post

	// read the size of each strand, poke the context offset of
	// each into STRAND_{SAVE,LOAD}_SWBASE now, no need to worry
	// about it later then.
	mov $r8 0x880
	shl b32 $r8 6
	iord $r9 I[$r8 + 0x000]		// STRANDS
	add b32 $r8 0x2200
	shr b32 $r14 $r15 8
	ctx_init_strand_loop:
		iowr I[$r8 + 0x000] $r14	// STRAND_SAVE_SWBASE
		iowr I[$r8 + 0x100] $r14	// STRAND_LOAD_SWBASE
		iord $r10 I[$r8 + 0x200]	// STRAND_SIZE
		shr b32 $r10 6
		add b32 $r10 1
		add b32 $r14 $r10
		add b32 $r8 4
		sub b32 $r9 1
		bra ne #ctx_init_strand_loop

	shl b32 $r14 8
	sub b32 $r15 $r14 $r15
	trace_clr(T_STRINIT)
	ret
')
